import  tensorflow as tf
from    tensorflow import keras
from    tensorflow.keras import layers, Sequential


#定义resnet基本模块
class BasicBlock(layers.Layer):

    def __init__(self, filter_num, stride=1):
        super(BasicBlock, self).__init__()

        self.conv1 = layers.Conv2D(filter_num, (3, 3), strides=stride, padding='same')  #3*3卷积核
        self.bn1 = layers.BatchNormalization()  #批次归一化
        self.relu = layers.Activation('relu')  #激活函数

        self.conv2 = layers.Conv2D(filter_num, (3, 3), strides=1, padding='same')#3*3卷积核
        self.bn2 = layers.BatchNormalization() #批次归一化
        #若stride!=1,跃迁线x也要减少同样的大小,strides=strides
        if stride != 1:
            self.downsample = Sequential()
            self.downsample.add(layers.Conv2D(filter_num, (1, 1), strides=stride)) #1*1卷积核
        else: #若stride=1, 直接加上x
            self.downsample = lambda x:x


    #正向传播
    def call(self, inputs, training=None):

        # [b, h, w, c]
        out = self.conv1(inputs)
        out = self.bn1(out)
        out = self.relu(out)

        out = self.conv2(out)
        out = self.bn2(out)

        identity = self.downsample(inputs)

        output = layers.add([out, identity])
        output = tf.nn.relu(output)

        return output

#创建Resnet类
class ResNet(keras.Model):


    def __init__(self, layer_dims, num_classes=100): # [2, 2, 2, 2]
        super(ResNet, self).__init__()
        #定义首个卷积层
        self.stem = Sequential([layers.Conv2D(64, (3, 3), strides=(1, 1)),
                                layers.BatchNormalization(),
                                layers.Activation('relu'),
                                layers.MaxPool2D(pool_size=(2, 2), strides=(1, 1), padding='same')
                                ])
        #定义各层的resblock模块
        self.layer1 = self.build_resblock(64,  layer_dims[0])
        self.layer2 = self.build_resblock(128, layer_dims[1], stride=2)
        self.layer3 = self.build_resblock(256, layer_dims[2], stride=2)
        self.layer4 = self.build_resblock(512, layer_dims[3], stride=2)

        # output: [b, 512, h, w],
        #全局池化，相当于展平
        self.avgpool = layers.GlobalAveragePooling2D()
        #全连接层
        self.fc = layers.Dense(num_classes)


    def call(self, inputs, training=None):

        x = self.stem(inputs)

        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.layer4(x)

        # [b, c]
        x = self.avgpool(x)
        # [b, 100]
        x = self.fc(x)

        return x


    #resblock层模块: 第一个基本模块strides=strides，第二个基本模块开始stride=1
    def build_resblock(self, filter_num, blocks, stride=1):  #blocks:该层有多少个基本resnt模块

        res_blocks = Sequential()
        # may down sample
        res_blocks.add(BasicBlock(filter_num, stride))   #卷积核=filter_num, stride=步长,第一个基本模块strides=strides

        for _ in range(1, blocks):
            res_blocks.add(BasicBlock(filter_num, stride=1))   #stride=1第二个基本模块开始stride=1

        return res_blocks


def resnet18():
    return ResNet([2, 2, 2, 2])  #首个卷积层+(2+2+2+2)*2+全连接层=18


def resnet34():
    return ResNet([3, 4, 6, 3]) #首个卷积层+(3+4+6+3)*2+全连接层=34